﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using GreyMagic;

namespace Hyperion
{
    /// <summary>
    /// Provides functionality for reading from and writing to memory while injected.
    /// </summary>
    public unsafe class InProcessMemory : Memory
    {
        public InProcessMemory(Process proc) : base(proc)
        {
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="address">The address.</param>
        /// <param name="isRelative">if set to <c>true</c> [is relative].</param>
        /// <returns></returns>
        public override T Read<T>(IntPtr address, bool isRelative = false)
        {
            return (T) Marshal.PtrToStructure(address, typeof (T));
        }

        /// <summary>
        /// Reads the specified addresses.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="isRelative">if set to <c>true</c> [is relative].</param>
        /// <param name="addresses">The addresses.</param>
        /// <returns></returns>
        public override T Read<T>(bool isRelative = false, params IntPtr[] addresses)
        {
            IntPtr tempAddress = addresses.Aggregate(IntPtr.Zero, (current, p) => Read<IntPtr>(current + (int) p));

            return Read<T>(tempAddress);
        }

        /// <summary>
        /// Writes to the specified address.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="address">The address.</param>
        /// <param name="value">The value.</param>
        /// <param name="isRelative">if set to <c>true</c> [is relative].</param>
        public override int Write<T>(IntPtr address, T value, bool isRelative = false)
        {
            var buffer = new byte[MarshalCache<T>.Size];

            fixed (byte* b = buffer)
                Marshal.StructureToPtr(typeof(T), new IntPtr(b), true);
            
            Marshal.Copy(buffer, 0, address, buffer.Length);

            return buffer.Length;
        }
    }
}